////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    IndianaJoanGuts.cpp										  //
//  Developer:																  //	
//  Create date:                                                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Indiana Joan.				  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        2011 - Riplife Gaming.  All rights reserved.						  //
////////////////////////////////////////////////////////////////////////////////

#include "IndianaJoanGuts.h"
#include "slotconn.h"
#include "sltsql.h"
#include <math.h>


static uint8 g_poolBonusPhase1[4][3] =
{
	// Pool 1
	{ 2, 4, 5 },
	// Pool 2
	{ 3, 5, 6 },
	// Pool 3
	{ 2, 4, 6 },	
	// Pool 4
	{ 2, 6, 6 }
};

static uint8 g_poolBonusPhase2_Spins[4][14] =
{
	// Pool 1
	{ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 5, 5 },
	// Pool 2
	{ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3 },
	// Pool 3
	{ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4 },	
	// Pool 4
	{ 1, 1, 1, 2, 2, 2, 4, 4, 4, 5, 5, 5, 6, 6 }
};

static uint8 g_poolBonusPhase2_Mults[4][14] =
{
	// Pool 1
	{ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2 },
	// Pool 2
	{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1 },
	// Pool 3
	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 },	
	// Pool 4
	{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 }
};


////////////////////////////////////////////////////////////////////////////
/// IndianaJoanGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Pears, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_ACE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_ACE.
////////////////////////////////////////////////////////////////////////////
IndianaJoanGuts::SpinResult IndianaJoanGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1			2					3						4						5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_JOAN */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_JOAN,		EVT_THREE_JOAN,			EVT_FOUR_JOAN,			EVT_FIVE_JOAN		},
/* ICON_GEEK */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_GEEK,			EVT_FOUR_GEEK,			EVT_FIVE_GEEK		},
/* ICON_RUBY */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_RUBY,			EVT_FOUR_RUBY,			EVT_FIVE_RUBY		},
/* ICON_TOOLS */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_TOOLS,		EVT_FOUR_TOOLS,			EVT_FIVE_TOOLS		},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_ACE,			EVT_FOUR_ACE,			EVT_FIVE_ACE		},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_KING,			EVT_FOUR_KING,			EVT_FIVE_KING		},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_QUEEN,		EVT_FOUR_QUEEN,			EVT_FIVE_QUEEN		},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_JACK,			EVT_FOUR_JACK,			EVT_FIVE_JACK		},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_TEN,			EVT_FOUR_TEN,			EVT_FIVE_TEN		},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_NINE,		EVT_THREE_NINE,			EVT_FOUR_NINE,			EVT_FIVE_NINE		},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			}
};


////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Scatter, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_SCATTER ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_SCATTER.
////////////////////////////////////////////////////////////////////////////
IndianaJoanGuts::SpinResult IndianaJoanGuts::m_aScatterWinSpinResults[][6] = 
{
// Number of icons:			0			1					2						3					4					5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_JOAN */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_GEEK */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_RUBY */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_TOOLS */		{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_LOSER,			EVT_SCATTER_NEAR_MISS,	EVT_THREE_SCATTER,	EVT_LOSER,			EVT_LOSER			}
};


////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::IndianaJoanGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
IndianaJoanGuts::IndianaJoanGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );

	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aScatterWinSpinResults array. 
	C_ASSERT( countof( m_aScatterWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::~IndianaJoanGuts()
///
/// Destructor for IndianaJoanGuts. 
////////////////////////////////////////////////////////////////////////////
IndianaJoanGuts::~IndianaJoanGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::SpinResult IndianaJoanGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
IndianaJoanGuts::SpinResult IndianaJoanGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 line, uint8 testIcon, bool& bSubstitution )
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Cauldron (Wild)...
		if ( currentIcon == ICON_WILD )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		
		// If the test icon is a Wild...
		// This is not possible in Indiana Joan. No WILDS in wheel 1
		/*
		if ( testIcon == ICON_WILD )
		{
			// then we still have a substitution.  In this case the line
			// started out with a Cauldron.
			bSubstitution = true;

			// We have now found our first icon that is not a Cauldron.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}
		*/

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			break;											
        }
    }

	// Use the array to look up the spin result for i testIcons.
  	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Declare a local variable to track whether or not we found a wild substitution.
	bool bSubstitution = false;

	// Icon in the first wheel (in Indiana Joan is NOT a wild, never: no wilds in first wheel)
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Get the result 
	rtnresult = getLineWinResultWithPossibleSubstitution( stops, line, testIcon, bSubstitution );

	// If we did actually have winning spin, apply possible multipliers and notes
	if ( rtnresult != EVT_LOSER )
	{
		if ( bSubstitution )
		{
			// then add a note.
			spinline->addNote( NOTE_WILD_SUBSTITUTION );
			multiplier *= MULT_WILD_SUBSTITUTION;
		}

		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			multiplier *= m_subGame->getData(SGDIDX_ORBITS_MULTIPLIER);
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		IndianaJoanGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) ||
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }

	// In third party games (API) we include the jackpot in every spin so that they can update it.
	// If no jackpot, they have not won the jackpot
	setJackpotAmount( getProgressiveValue() );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::getScatterWinResult()   
///
/// @param   spin				The current spin object.
/// @param   testIcon			The scatter icon that we are searching for.
/// @param   scatterPositions	The bit mask for the scatters positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
IndianaJoanGuts::SpinResult IndianaJoanGuts::getScatterWinResult( SlotSpin* spin, uint8 testIcon, int32& scatterPositions ) const
{
	// Declare a local variable to keep up with the icon count.
	uint8 numScatterIcons = 0;

	// Initialize the scatterPositions.
	scatterPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == testIcon )
			{
				// then increase the icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for numScatterIcons.
	return lookupScatterWinSpinResult( testIcon, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Initialize our multiplier for scatters.
		int32 multiplier = 1;
		
		// If we are in free spin mode...		
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			multiplier = m_subGame->getData(SGDIDX_ORBITS_MULTIPLIER);
		}

		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;

		// Add the payout (with multiplier and bet per line) to the total win for this spin.
		increaseTotalWin( nPayout * multiplier * getBetPerLine() * getNumLinesBet() );
    
		// Add a scatter to the spin.
		spin->addScatter( scatterPositions, nPayout * getBetPerLine() * getNumLinesBet(), multiplier, false, srScatterResult, 0 );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::classifyScatters( SlotSpin* spin )
{	
	// By default, this is not a reference game number
	m_bReferenceGamenumber = false;
	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// See if there is a Wand scatter.
	SpinResult srResult = getScatterWinResult( spin, ICON_SCATTER, nScatterPositions );

	// Add the scatter for Wand, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	//  If three or more scatters, activate bonus game
	if (srResult == EVT_THREE_SCATTER)
	{
		if ( (getGameModeId() == MODE_FREE_SPIN) && (getFreeSpins() > 1) ) 
		{
			// We are in bonus game: retrigger it. Launch bonus game (just phase 1)
			m_subGame->update( SGIDX_BONUS_GAME_STATE, ijPhase1_DoorsOnly );
		}
		else
		{
			// Last spin of a free spins round: this is the reference game number for the new free spins round
			// Before this modification, a free spin could never be a reference game number
			m_bReferenceGamenumber = true;
			
			// Normal spin or last spin of a free spins round
			if (m_subGame->getData(SGDIDX_BONUS_PENDING) > 0)
			{
				// There are bonus pending: retrigger it. Launch bonus game (just phase 1)
				m_subGame->update( SGIDX_BONUS_GAME_STATE, ijPhase1_DoorsOnly );
			}
			else
			{
				// No bonus pending: Launch bonus game (complete: phase 1 + phase 2)
				m_subGame->update( SGIDX_BONUS_GAME_STATE, ijPhase1_Doors );
			}	
		}
	}
	else if ((getGameModeId() == MODE_FREE_SPIN) && (getFreeSpins() == 1))
	{
		// If this is the last free spin and bonus has to be retriggered, activate bonus game phase 2
		uint16 nBonusPending = m_subGame->getData(SGDIDX_BONUS_PENDING);
		if (nBonusPending > 0)
		{
			uint16 nOrbitsToPick = m_subGame->getData(SGDIDX_BONUS_PENDING_BASE);
			m_subGame->update(SGDIDX_NUM_ORBITS_PICK, nOrbitsToPick);
			m_subGame->setServerOnly(SGDIDX_NUM_ORBITS_PICK, false);

			// If more pending bonus, reorganize subgame data items
			removePendingBonus();

			// Last spin of a free spins round: this is the reference game number for the new free spins round
			// Before this modification, a free spin could never be a reference game number
			m_bReferenceGamenumber = true;
			
			// Launch bonus game (just phase 2)
			m_subGame->update( SGIDX_BONUS_GAME_STATE, ijPhase2_Orbits );
		}
	}
	else
	{
		m_subGame->update( SGIDX_BONUS_GAME_STATE, ijNone );  
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 IndianaJoanGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range. GENESIS GAMES: [9000,9500)
    return ( getGutsId() >= 9000 && getGutsId() < 9500 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden when running in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::preSpinClassifyResults( SlotSpin* spin )
{
	SlotGuts::preSpinClassifyResults(spin);

	if (getGameModeId() == MODE_NORMAL)
	{
		// If we are in normal mode, all subgame data is hidden
		m_subGame->setServerOnly(SGDIDX_DOORS_CONTENT, true);
		m_subGame->setServerOnly(SGDIDX_BONUS_PENDING, false);
		m_subGame->setServerOnly(SGDIDX_NUM_ORBITS_PICK, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_1, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_2, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_3, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_4, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_5, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_6, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_7, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_8, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_9, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_10, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_11, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_12, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_13, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_14, true);
		m_subGame->setServerOnly(SGDIDX_ORBITS_FREE_SPINS, true);
		m_subGame->setServerOnly(SGDIDX_ORBITS_MULTIPLIER, true);
	}
	else
	{
		// If we are in free spins mode, just two subgame items are important to the client:
		// SGDIDX_BONUS_PENDING -> if bonus is retriggered
		// SGDIDX_ORBITS_MULTIPLIER -> multiplier applied to all wins
		m_subGame->setServerOnly(SGDIDX_DOORS_CONTENT, true);
		m_subGame->setServerOnly(SGDIDX_BONUS_PENDING, false);
		m_subGame->setServerOnly(SGDIDX_NUM_ORBITS_PICK, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_1, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_2, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_3, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_4, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_5, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_6, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_7, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_8, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_9, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_10, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_11, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_12, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_13, true);
		m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_14, true);
		m_subGame->setServerOnly(SGDIDX_ORBITS_FREE_SPINS, true);
		m_subGame->setServerOnly(SGDIDX_ORBITS_MULTIPLIER, false);
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::postSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to see if we can complete the game.
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::postSpinClassifyResults( SlotSpin* spin )
{
	// If we are in a bonus game, the spin cannot complete
	if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) > 0)
	{
		initializeSubGame( m_subGame->getData(SGIDX_BONUS_GAME_STATE) );
		setGameCanComplete( false );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved gamees.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 IndianaJoanGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=0;
	pStops[1]=0;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}

int32 IndianaJoanGuts::getProgressiveValue()
{
   SlotsSQL *slotTracker = (SlotsSQL *) tracker;
   int32 jackpot = 0;

    if ( tracker )
    {
    	slotTracker->lock();
		slotTracker->getJackpot(getJackpotId(), jackpot, getInfo()->forMoney, 1);
    	slotTracker->unlock();
    }

	return jackpot;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 IndianaJoanGuts::processClientData()
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
////////////////////////////////////////////////////////////////////////////
bool8
IndianaJoanGuts::processClientData( uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData )
{
	// Start by assuming that we do not need to reply to this message
	bool8 bReply = false;

	if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) == ijPhase1_Doors )
	{
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			int32 nDoor = *bonusData;
			if (nDoor >= 1 && nDoor <= 3)
			{
				// User has chosen a door. Send doors to client and apply number of free spins won
				m_subGame->setServerOnly(SGDIDX_DOORS_CONTENT, false);
				m_subGame->setServerOnly(SGDIDX_NUM_ORBITS_PICK, false);

				// Go to next phase (orbits)
				initializeSubGame( ijPhase2_Orbits );
				m_subGame->update( SGIDX_BONUS_GAME_STATE, ijPhase2_Orbits );
				
				bReply = true;
			}
		}
	} 
	else if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) == ijPhase1_DoorsOnly )
	{
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			int32 nDoor = *bonusData;
			if (nDoor >= 1 && nDoor <= 3)
			{
				// User has chosen a door. Send doors to client (bonus will not start now)
				m_subGame->setServerOnly(SGDIDX_DOORS_CONTENT, false);
				m_subGame->setServerOnly(SGDIDX_NUM_ORBITS_PICK, false);
				m_subGame->setServerOnly(SGDIDX_BONUS_PENDING, false);
				
				// Two possibilities here:
				if (getFreeSpins() > 0)
				{
					// 1) This doors only bonus was triggered in the middle of the free spins round. So spins continue
					// Continue spins
					m_subGame->update( SGIDX_BONUS_GAME_STATE, ijNone );
					setGameCanComplete( true );
				}
				else
				{
					// 2) This doors only bonus was triggered in the LAST free spins. In this case we have to go to bonus phase 2,
					//    because there was a bonus pending previous to this one

					// Update values
					uint16 nOrbitsToPick = m_subGame->getData(SGDIDX_BONUS_PENDING_BASE);
					m_subGame->update(SGDIDX_NUM_ORBITS_PICK, nOrbitsToPick);
					m_subGame->setServerOnly(SGDIDX_NUM_ORBITS_PICK, false);

					// If more pending bonus, reorganize subgame data items
					removePendingBonus();
	
					// Go to phase 2 (orbits)
					initializeSubGame( ijPhase2_Orbits );
					m_subGame->update( SGIDX_BONUS_GAME_STATE, ijPhase2_Orbits );
				}
				
				bReply = true;
			}
		}
	} 
	else if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) == ijPhase2_Orbits )
	{
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			int32 nOrbit = *bonusData;
			if (nOrbit >= 1 && nOrbit <= 14)
			{
				// Orbits in subgame data are 0-based
				uint8 iIndex = SGDIDX_ORBIT_CONTENT_1 + nOrbit - 1;
				// Only process if the orbit was not chosen before (if it was chosen before, it was sent to the client, so server only is false)
				if (m_subGame->isServerOnly(iIndex))
				{
					// Get the orbit information, and update subgame data
					int32 nOrbitInfo = m_subGame->getData(iIndex);
					uint8 nFreeSpinsWon = nOrbitInfo / 10;
					uint8 nMultiplierWon = nOrbitInfo % 10;

					// Update subgame information (free spins won and multiplier)
					int32 nFreeSpinsTotal = m_subGame->getData(SGDIDX_ORBITS_FREE_SPINS);
					m_subGame->update(SGDIDX_ORBITS_FREE_SPINS, nFreeSpinsTotal + nFreeSpinsWon);
					if (nMultiplierWon > 0)
					{
						int32 nMultiplierTotal = m_subGame->getData(SGDIDX_ORBITS_MULTIPLIER);
						m_subGame->update(SGDIDX_ORBITS_MULTIPLIER, nMultiplierTotal + nMultiplierWon);
					}
					
					// Send orbit information to the client. If this is last orbit to be chosen, send all of them
					int32 nOrbitsToPick = m_subGame->getData(SGDIDX_NUM_ORBITS_PICK) - 1;
					m_subGame->update(SGDIDX_NUM_ORBITS_PICK, nOrbitsToPick);
					if (nOrbitsToPick > 0)
					{
						m_subGame->setServerOnly(iIndex, false);
					}
					else
					{
						// Send all of them, and end bonus game
						for (int i=0; i<=14; i++)
						{
							m_subGame->setServerOnly(SGDIDX_ORBIT_CONTENT_1 + i, false);
						}

						// Add bonus game (free spins won, multiplier)
						addBonusGame(1, 0, 0, m_subGame->getData(SGDIDX_ORBITS_MULTIPLIER), 0, m_subGame->getData(SGDIDX_ORBITS_FREE_SPINS)); 

						// Go to free spins mode
						incrementFreeSpins( m_subGame->getData(SGDIDX_ORBITS_FREE_SPINS) );
						setGameModeId( MODE_FREE_SPIN );

						// End bonus game
						m_subGame->update( SGIDX_BONUS_GAME_STATE, ijNone );
						setGameCanComplete( true );
					}

					bReply = true;
				}
			}
		}
	}
	
 	
	return bReply;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ReelBloodGuts::initializeSubGame( )
///
/// Initialize subgame data
////////////////////////////////////////////////////////////////////////////
void IndianaJoanGuts::initializeSubGame ( uint8 Phase )
{
	uint8 iPool;
	
	if (Phase == ijPhase1_Doors)
	{
		iPool = getRandomValueFromProbabilityTable( PT_BONUS_DOORS );
		// Phase 1 + Phase 2
		generateDoors(iPool, false);
	}
	else if (Phase == ijPhase1_DoorsOnly)
	{
		iPool = getRandomValueFromProbabilityTable( PT_BONUS_DOORS );
		// Just phase 1
		generateDoors(iPool, true);
	}
	else
	{
		// Phase 2
		iPool = getRandomValueFromProbabilityTable( PT_BONUS_ORBITS );
		generateOrbits(iPool);
	}
}

void IndianaJoanGuts::generateDoors(uint8 Pool, bool8 Phase1Only)
{
	// Initialize doors contents (we can't send it to the client yet)
	m_subGame->setServerOnly(SGDIDX_DOORS_CONTENT, true);
	m_subGame->setServerOnly(SGDIDX_NUM_ORBITS_PICK, true);
	//m_subGame->setServerOnly(SGDIDX_BONUS_PENDING, true);
	
	// Once we have chosen a pool, we have to disorder it and store it in the subgame data
	int32 g_AuxPool[3];
	// First we copy the chosen pool
	for(int i=0; i<3; i++)
	{
		g_AuxPool[i] = g_poolBonusPhase1[Pool-1][i];
	}
	// Then we disorder it
	for(int i=0; i<3; i++)
	{
		int32 iAux = g_AuxPool[i];
		
		// Choose a value to swap with
		uint8 iRandomPosition = getrand() % 3;
		
		// Swap the values
		g_AuxPool[i] = g_AuxPool[iRandomPosition];
		g_AuxPool[iRandomPosition] = iAux;
	}

	// Door content: XYZ. X=door chosen by user; Y=first door not chosen; Z=second door not chosen
	int32 iDoorsContent = (g_AuxPool[0]*100) + (g_AuxPool[1]*10) + g_AuxPool[2];

	// Once we have disordered the values, store them in the subgame data
	m_subGame->update(SGDIDX_DOORS_CONTENT, iDoorsContent);

	// Orbits to pick?
	if (Phase1Only)
	{
		// We are not starting the orbits bonus now, just store result of door
		addPendingBonus(g_AuxPool[0]);
	}
	else
	{
		// We are starting orbits bonus now
		m_subGame->update(SGDIDX_NUM_ORBITS_PICK, g_AuxPool[0]);
	}
		
}

void IndianaJoanGuts::generateOrbits(uint8 Pool)
{
	// Initialize doors contents (we can't send it to the client yet)
	for(int i=0; i<14; i++)
	{
		// Orbit content: XY. X=num of free spins; Y=multiplier
		uint8 iOrbitIndex = SGDIDX_ORBIT_CONTENT_1 + i;
		m_subGame->setServerOnly(iOrbitIndex, true);
	}
	
	// Once we have chosen a pool, we have to disorder it and store it in the subgame data
	int32 g_AuxPool_FreeSpins[14];
	int32 g_AuxPool_Multipliers[14];

	// First we copy the chosen pool
	for(int i=0; i<14; i++)
	{
		g_AuxPool_FreeSpins[i] = g_poolBonusPhase2_Spins[Pool-1][i];
		g_AuxPool_Multipliers[i] = g_poolBonusPhase2_Mults[Pool-1][i];
	}

	// Then we disorder it
	for(int i=0; i<14; i++)
	{
		int32 iAux_FreeSpins = g_AuxPool_FreeSpins[i];
		int32 iAux_Multipliers = g_AuxPool_Multipliers[i];
		
		// Choose a value to swap with
		uint8 iRandomPosition = getrand() % 14;
		
		// Swap the values
		g_AuxPool_FreeSpins[i] = g_AuxPool_FreeSpins[iRandomPosition];
		g_AuxPool_FreeSpins[iRandomPosition] = iAux_FreeSpins;
		g_AuxPool_Multipliers[i] = g_AuxPool_Multipliers[iRandomPosition];
		g_AuxPool_Multipliers[iRandomPosition] = iAux_Multipliers;
	}

	// Once we have disordered the values, store them in the subgame data
	for(int i=0; i<14; i++)
	{
		// Orbit content: XY. X=num of free spins; Y=multiplier
		uint8 iOrbitIndex = SGDIDX_ORBIT_CONTENT_1 + i;
		int32 iOrbitContent = (g_AuxPool_FreeSpins[i]*10) + g_AuxPool_Multipliers[i];
		m_subGame->update(iOrbitIndex, iOrbitContent);
	}

	// We start with 3 free spins and a multiplier of 2
	m_subGame->update(SGDIDX_ORBITS_FREE_SPINS, 3);
	m_subGame->update(SGDIDX_ORBITS_MULTIPLIER, 2);
	m_subGame->setServerOnly(SGDIDX_ORBITS_FREE_SPINS, false);
	m_subGame->setServerOnly(SGDIDX_ORBITS_MULTIPLIER, false);

}

// CGC 24082001. Sometimes we want a free game to be the reference game number (for example, Indiana Joan)
// By default it will be false, it has to be redefined in games needed
bool8 
IndianaJoanGuts::getForceReferenceGameNumber()
{
	return m_bReferenceGamenumber;
}

void IndianaJoanGuts::removePendingBonus()
{
	// If pending bonus:
	// Next pending bonuses (number of orbits to pick) in SGDIDX_BONUS_PENDING_BASE, SGDIDX_BONUS_PENDING_BASE+1, ... 
	uint8 nBonusPendingAux = 0;
	uint8 iBonusIndex = 0;
	do
	{
		nBonusPendingAux = m_subGame->getData(SGDIDX_BONUS_PENDING_BASE + iBonusIndex + 1);
		m_subGame->update(SGDIDX_BONUS_PENDING_BASE + iBonusIndex, nBonusPendingAux);
		iBonusIndex++;
	} while (nBonusPendingAux > 0);
	
	// Pending--
	uint16 nBonusPending = m_subGame->getData(SGDIDX_BONUS_PENDING);
	m_subGame->update(SGDIDX_BONUS_PENDING, nBonusPending-1);
}

void IndianaJoanGuts::addPendingBonus(int32 Orbits)
{
	int8 iIndex = -1;
	uint8 nPendingBonusAux = 0;
	do 
	{
		iIndex++;
		nPendingBonusAux = m_subGame->getData(SGDIDX_BONUS_PENDING_BASE + iIndex);
	} while (nPendingBonusAux != 0);

	m_subGame->update(SGDIDX_BONUS_PENDING_BASE + iIndex, Orbits);
	m_subGame->setServerOnly(SGDIDX_BONUS_PENDING_BASE + iIndex, true);

	// Pending++
	uint16 nNumberPending = m_subGame->getData(SGDIDX_BONUS_PENDING);
	m_subGame->update(SGDIDX_BONUS_PENDING, nNumberPending+1);
}